#include "SysInput.h"

const FLOAT WALK_SPEED					= 5.75f;		// Character walk speed.
const FLOAT SNEAK_SPEED					= 3.0f;			// Character sneak speed.
const FLOAT RUN_SPEED					= 12.0f;		// Character run speed.

const FLOAT MOUSE_DOLLY_SENSITIVITY		= 0.012f;		// Mouse sensitivity for dollying the camera.
const FLOAT MOUSE_ROTATION_SENSITIVITY	= 0.15f;		// Mouse sensitivity for rotating the camera.

const FLOAT CNTRL_DOLLY_SENSITIVITY		= 3.5f;			// Controller sensitivity for dolling the camera.

//-------------------------------------------------------------------------------------------------------------------
SysInput::SysInput()
{ 
	m_last_mouse_pos.x = 0;
	m_last_mouse_pos.y = 0;
}

//-------------------------------------------------------------------------------------------------------------------
SysInput::~SysInput()
{ 
	SafeDelete(m_pInput);
	SafeDelete(m_pController);
}

//-------------------------------------------------------------------------------------------------------------------
XMFLOAT3 SysInput::GetVelocity()
{
	return m_inputVelocity;
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::Initialise(Camera* pCamera, HINSTANCE hInstance, HWND hwnd, int screenWidth, int screenHeight)
{
	m_pInput			= new InputClass();
	m_pInput->Initialise(hInstance, hwnd, screenWidth, screenHeight);

	m_pController		= new ControllerInput();
	m_controllerEnabled = m_pController->Initialise();

	m_pCamera			= pCamera;
	m_dollyOut			= false;

	m_inputVelocity		= XMFLOAT3(0,0,0);

	m_bPrevThirdPerson	= false;
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::Update(float dt, float* distInc)
{
	m_pInput->Update();

	m_inputVelocity = XMFLOAT3(0.0f,0.0f,0.0f);

	UpdateKeyboardInput(dt);

	UpdateMouseInput();

	UpdateControllerInput(dt, m_controllerEnabled, distInc);
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::UpdateKeyboardInput(float dt)
{
	//============================================
	// Walk / Sneak / Run Controls
	//============================================

	if(m_pInput->IsKeyPressed(DIK_W))
	{
		if(m_pInput->IsKeyPressed(DIK_LSHIFT))				// Sneaking Forward
		{
			m_pCamera->Walk(SNEAK_SPEED * dt, m_inputVelocity);
		}
		else if (m_pInput->IsKeyPressed(DIK_LCONTROL))		// Running Forward
		{
			m_pCamera->Walk(RUN_SPEED * dt, m_inputVelocity);
		}
		else												// Walking Forward
		{
			m_pCamera->Walk(WALK_SPEED * dt, m_inputVelocity);
		}
	}
	
	if(m_pInput->IsKeyPressed(DIK_S))
	{
		if(m_pInput->IsKeyPressed(DIK_LSHIFT))				// Sneaking Backward
		{
			m_pCamera->Walk(-SNEAK_SPEED * dt, m_inputVelocity);
		}
		else if(m_pInput->IsKeyPressed(DIK_LCONTROL))		// Running Backward
		{
			m_pCamera->Walk(-RUN_SPEED * dt, m_inputVelocity);
		}
		else												// Walking Backward
		{
			m_pCamera->Walk(-WALK_SPEED * dt, m_inputVelocity);
		}
	}

	if(m_pInput->IsKeyPressed(DIK_A))
	{
		if (m_pInput->IsKeyPressed(DIK_LSHIFT))				// Sneaking Right
		{
			m_pCamera->Strafe(SNEAK_SPEED * dt, m_inputVelocity);
		}
		else if (m_pInput->IsKeyPressed(DIK_LCONTROL))		// Running Right
		{
			m_pCamera->Strafe(RUN_SPEED * dt, m_inputVelocity);
		}
		else												// Walking Right
		{
			m_pCamera->Strafe(WALK_SPEED * dt, m_inputVelocity);
		}
	}
	
	if(m_pInput->IsKeyPressed(DIK_D))
	{
		if (m_pInput->IsKeyPressed(DIK_LSHIFT))				// Sneaking Left
		{
			m_pCamera->Strafe(-SNEAK_SPEED * dt, m_inputVelocity);
		}
		else if(m_pInput->IsKeyPressed(DIK_LCONTROL))		// Running Left
		{
			m_pCamera->Strafe(-RUN_SPEED * dt, m_inputVelocity);
		}
		else												// Walking Left
		{
			m_pCamera->Strafe(-WALK_SPEED * dt, m_inputVelocity);
		}
	}

	//============================================
	// First/Third Person Toggle
	//============================================

	if(m_pInput->IsKeyPressed(DIK_F))
		m_pCamera->ToggleFirstPerson();

	if(m_pInput->IsKeyPressed(DIK_T))
		m_pCamera->ToggleThirdPerson();
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::UpdateMouseInput()
{
	// View Controls (separate method)

	int x, y;
	m_pInput->GetMouseLocation(x,y);

	if (m_pInput->IsButtonPressed(0))
	{
		// Make each pixel correspond to a quarter of a degree.
		float dx = XMConvertToRadians(MOUSE_ROTATION_SENSITIVITY * static_cast<float>(x - m_last_mouse_pos.x));
		float dy = XMConvertToRadians(MOUSE_ROTATION_SENSITIVITY * static_cast<float>(y - m_last_mouse_pos.y));

		// Update angles based on input to orbit camera around box.
		m_pCamera->UpdateRotation(dx, dy);
	}

	// Update the camera distance only if it's in third-person mode.
	if (m_pCamera->IsThirdPerson())
	{
		if (m_pInput->IsButtonPressed(1))
		{
			// Make each pixel correspond to 0.005 unit in the scene.
			float dx = MOUSE_DOLLY_SENSITIVITY * static_cast<float>(x - m_last_mouse_pos.x);
			float dy = MOUSE_DOLLY_SENSITIVITY * static_cast<float>(y - m_last_mouse_pos.y);

			// Update the camera radius based on input.
			m_pCamera->UpdateDistance((dx - dy)*-1.0f);
		}
	}

	m_last_mouse_pos.x = x;
	m_last_mouse_pos.y = y;
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::UpdateControllerInput(float dt, bool bControllerEnabled, float* distInc)
{
	// Controller input.
	
	if (bControllerEnabled)
	{
		m_pController->Update();

		//=================================================
		//	Apply Camera Rotation
		//=================================================

		float theta = (m_pController->get_thumbRX() * dt) * -1;
		float phi   = m_pController->get_thumbRY() * dt;
		
		m_pCamera->UpdateRotation(theta, phi);

		//=================================================
		//	Apply Walking
		//=================================================
		// Note: Walk and Strafe values are scaled such that 
		// it produces the same speed as the keyboard input.

		float walk   = m_pController->get_thumbLY() * dt;
		float strafe = (m_pController->get_thumbLX() * dt) * -1;
		
		if (fabs(walk) > (5000.0f*0.00008f*dt))
		{
			if (m_pController->get_rightShoulder())
			{
				walk *= (RUN_SPEED * 0.6085f);
			}
			else if (m_pController->get_leftShoulder())
			{
				walk *= (SNEAK_SPEED * 0.6085f);
			}
			else
			{
				walk *= (WALK_SPEED * 0.6085f);
			}

			m_pCamera->Walk(walk, m_inputVelocity);
		}

		//=================================================
		//	Apply Strafing
		//=================================================

		if (fabs(strafe) > (5000.0f*0.00008f*dt))
		{
			if (m_pController->get_rightShoulder())
			{
				strafe *= (RUN_SPEED * 0.6085f);
			}
			else if (m_pController->get_leftShoulder())
			{
				strafe *= (SNEAK_SPEED * 0.6085f);
			}
			else
			{
				strafe *= (WALK_SPEED * 0.6085f);
			}

			m_pCamera->Strafe(strafe, m_inputVelocity);
		}

		//=================================================
		//	Apply Dollying
		//=================================================
		
		if (m_pCamera->IsThirdPerson())
		{
			float distance_in	= (m_pController->get_leftTrigger()  * dt) * CNTRL_DOLLY_SENSITIVITY;
			float distance_out	= (m_pController->get_rightTrigger() * dt) * CNTRL_DOLLY_SENSITIVITY;
			float distance		= distance_in + distance_out;

			// Check if we are dollying out. If so, send information back to the calling code and return.
			// We need to do some further processing to determine if we can dolly out.
			if (distance_in)
			{
				*distInc = distance;	// Send the desired distance value - to be set if there is no collision.
				m_dollyOut = true;

				return;	// Immediately return if we are dollying out
			}
			else
			{
				// Update camera distance.
				m_pCamera->UpdateDistance(distance);
			}

			m_dollyOut = false;
		}

		//============================================
		// First/Third Person Toggle
		//============================================

		if (m_pController->get_buttonA() == true)
		{
			m_pCamera->ToggleThirdPerson();
		}

		if (m_pController->get_buttonX() == true)
		{
			m_pCamera->ToggleFirstPerson();
		}
	}
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::NormaliseVelocity()
{
	XMFLOAT3 vec;
	vec.x = m_inputVelocity.x;	vec.y = 0.0f; vec.z = m_inputVelocity.z;
	XMVECTOR v = XMLoadFloat3(&vec);
	
	XMVector3Normalize(v);

	XMStoreFloat3(&m_inputVelocity, v);

}

//-------------------------------------------------------------------------------------------------------------------
bool SysInput::GetDollyOut() const
{
	return m_dollyOut;
}

//-------------------------------------------------------------------------------------------------------------------
void SysInput::SetDollyOut(bool dolly)
{
	m_dollyOut = dolly;
}